let a: string = "";
console.log(a);

/**
 * 基础类型
 * 类型不一定都需要加上靠类型推导就行，主要还是要安全。
 * number Number的区别 前面是基础类型后面是类类型
 */
let base_numer: Number = 1; // 装箱
let base_number2: Number = new Number(1);

let tupl: [number, string] = [1, "cc"];
tupl.push(""); // 这样玩元组是挡不住的。 下面那个意思一样的你可以放 string | number  里面的任何一个 长度可以突破限制。
let x = tupl.pop(); // string | number | undefined 的联合类型

// tupl[3] = 1 这么不可以就很好理解 因为只有两个和push区分。方法可以突破 

// 数据交换会用到元组   <T,B> [B,T]


// 枚举类型 【普通枚举】、【常量枚举】、【异构枚举】
enum ROLE { // 普通枚举编译完了就是个对象。互为反射 ROLE[ROLE['a']=0] = 'a'; 互为反射只有索引的才可以。
    a, // 0
    b, // 1
    c, // 2
}

// enum ROLE2 { 【异构枚举】
//     a = ''
//     b // 这种事不可以的第一个非数字这里没办法自增。
// }


// 常量枚举
const enum ROLE3 { // 打包出来就没有ROLE3这个对象了。直接用数字，减少容量。
    A
}


// null undefined 是任何类型的子类型， 但是是在非严格模式下。
let as:number;
// as = null; 严格模式就报错。 tsconfig strict: false 或者 strictNullChecks: false


// 注意一个问题如果当前ts文件没有export 那么这个文件就作为全局使用防止冲突就加上export 作为私有化。 export {} 一下就完事儿。 这个也能防止合并比如namespace 函数等等。


// never永远不的意思 while(true){} throw new Error("") 做switch 最后如果没没判断的挺好 永远走不到判断。
function neverCallFunc(type:string){
    if(typeof type == 'string') {

    } else {
        // 永远走不到。
        console.log(type) // (parameter) type: never 可以做一个代码完整校验
    }
}

function neverCallFunc2():never {
    throw new Error("ak")
}

function neverCallFunc3():never{
    while(true) {

    }
}

let neverCallFuncS:string = neverCallFunc2(); // never可以赋值给其它类型。


// void 智能接受 undefined 和 null // 同样的需要strict: false 或者 strictNullChecks: false undefined默认是可以的。


// Object非原始类型。 范型约束用Object用的多一些。
function createObj(obj:object) {

}
createObj({});



/**
 * Symbol 当key用防止key重复 这个东西编译不了es5
 * 
 * 
 * BigInt
 */

let symbol1:symbol = Symbol();
let symbol2:symbol = Symbol();

// symbol1 != symbol2

// let maxInt:bigint = BigInt(Number.MAX_SAFE_INTEGER); 依赖高级es库  es10



// 联合类型
let strNumber: number | string;
// 默认是可以调用 number 和 string 的公共方法。
// 确认了类型才能调用类型对应的方法。
strNumber  = 1;
// strNumber.toFixed
strNumber = '';
// strNumber.charAt
// 当你赋值完了他会自动的去算你这是个什么，根据最近一次的赋值来确定类型。

// HTMLElement ts包里面的内置类型
const ele = document.getElementById("sqx")!; 
// ele.innerHTML = 'admin'
// ele!.innerHTML = 'admin';

// 强转
// const fele:HTMLElement = ele as HTMLElement;

// <HTMLElement> ele 这个有兼容性问题

interface SHIT{}

const shit:SHIT = <SHIT> ele;


// 在联合类型中强转的必须 联合类型中更有才行  string | number | boolean 强转的必须是这三个中的任何一个。
// ts 一切是以安全为基准的 所以必须是 联合类型中有的才能行。 <xxx> jsx语法使用会有问题。 用 as 好
let bug:number|string = 1;
// (bug as boolean) 报错
// (bug as any) as boolean 这是可以的
// (bug as unknown) as boolean  这也是可以的 双重断言。 会导致类型出问题
// ?. 空说的是undefined null ?? false 说的是真 只有undefined null 才会走到后面去。 


// 字面量类型。
let zml:'zml'|'yaoxi';
// 类型别名 type TAlias = 'a'|'b'   


// 函数增加类型。
// function 方式 表达式方式
// 函数类型是function sum(x: number, y: string): string
function sum(x:number, y:string):string {
    return x+y;
}


// 默认值和可选参数不能一起用
// y?:number = 1 不行
let sum2 = (x:number, y?:number, ...args:number[]):string => {
    return `${x}${y}==>>>`
}


// 函数重载
// 一对一 传入 string 返回 string[]
// ..
function toArray(val:string):string[]
function toArray(val:number):number[]
function toArray(val:any):any {
    return []
}

let s = toArray(1);



// 类
class CT {
    xa!:string; // 不用飞需要初始化直接加个!完事儿。
    // x:string;
    
    // 这个东西不能重栽。
    constructor(public x:string){

    }
    say(){
        
    }
}

class CT2 extends CT {
    public readonly mq:number; // 这个必须被初始化 在声明的时候或者在构造函数的时候。
    constructor(m:number){
        super('');
        // super.x
        this.mq = m;
    }
    static say(){
        // console.log(super.x);
    }

    // 原型方法。
    ksay(){

    }

    // 把属性定义在原型上
    // 这个就扔到defineproperty
    get name(){
        return 'name';
    }

    set name(val:string){
        //...
    }
}

// 修饰符 constructor(public x:string) 可以省略初始化 this.xxxx public protected private readonly 
// 子类如果继承了父类如果子必须super一下 
// 那边相当于用了 super = {}  xxx.call(super, '', '') 这么个过程。
// 构造函数 private 是不能被new 和 继承的。 protected constructor 可以被继承但是不能被new 
// readonly 如何修饰的是对象，对象内部是可以改的。


// 静态属性和方法。static es6不支持静态属性 es7支持。 es6静态属性需要用属性访问器 static get name(){....} 这个编译后的就是defineproperty
// 调用方法通过函数.xxx 也就是类.xxx
// 静态方法的特点
/**
 * 1.静态方法可以被继承
 * 通过__proto__ 链实现的。
 * 调用父的东西可以通过 super.xxx来调用。
 * 
 * 2. 可以通过super.x super.staticx 来调用父类的静态和非静态
 * 
 * 3. 静态方法中只能用静态的。
 */


// 原型上的属性怎么定义。


/**
 * 装饰器   
 * 是一个实验性的属性
 * 可以扩展 类、属性、方法
 * 
 * tsconfig
 * experimentalDecorators true
 * 
 * 装饰器必须是一个函数。
 */

function say(){
    return (target:any, r:any)=>{
        // 给 target 原型上增加一些功能
        target.prototype.hi = function() {
            console.log('hi');
            // 通过装饰器装饰的在new的时候没有语法提示。 这个玩意是后加的非语法阶段了，在执行阶段了。
            // 可以在PSAY预先声明一下
        }
    }
}
function say1(){
    return (target:any, r:any)=>{}
}
function say2(){
    return (target:any, r:any)=>{}
}
@say()
@say1()
@say2()
class PSAY { // 装饰器的执行顺序是从say2 -> say1 -> say 从后往前能。 初始化是 say -> say1 -> say2 执行就反过来。
    hi!:()=>void // 后面可以做声明合并。
}


// 修饰属性
// target 类的原型 相当于直接就是xxx.prototype key 属性名字
function xsSX(target:any, key:any){
    let val:any = '';
    // 给原型的 key 这个属性定义
    // 拦击器会优先实例的属性 
    // 拦击器 -> 实例 -> 原型链
    Object.defineProperty(target, key, {
        get(){
            // 读取的时候走这个 this.x
            return val.toUpperCase() ;

        },
        set(newVal){
            // 设置的时候就走这个 this.x = 1s

            val = newVal;
        }
    })

}

class SX {
    @xsSX // 执行 xsSX 的时候 x赋值了吗？ 是先执行 xsSX 下面那个还没有初始化。
    public x:number = 1;
}


// 修饰静态属性
// target 类
function dxsjtSx(target:any, key:any){
    let val:any = '';
    Object.defineProperty(
        target,
        key,
        {
            get(){
                return val * 2;
            }
        }
    )
}

// target 原型类 key 方法名字 des 属性描述器
function decFunc(target:any, key:any){
    Object.defineProperty(target, key, {
        writable: false, // 修改属性访问属性。
    })
}


function mmp(target:any, key:any, idx:number){

}

class xsjtSX {
    @dxsjtSx
    static age:number = 18


    // @decFunc
    // say(@mmp a:number){

    // }
}

// 关于target class static 都是类
// 里面的都是 xxx.prototype的


// 接口
/**
 * 1.接口可以被类继承和实现
 * 2.obj:{name:string} 这么直接描述是可以的
 * 3.描述函数
 * 4.接口的特性
 */
// function getObjs(obj:{name:string}) {

// }

interface IFunc{
    ():void;
}

interface IJSQ {
    count:number;
    ():void;
}

let jsq:IJSQ = ((() => {
    jsq.count++;
}) as IJSQ) // 报错直接断言 
jsq.count = 0;


// 接口的特性
// 属性签名满足 '': ''就行了
// 不满足接口的约束的时候可以直接 as 就是那这个 多的可以直接断言 但是必须满足接口必须的类目
interface JKTX {
    [x:string]: any
}

interface TSED {
    a:string;
}

// 接口的合并 接口同名就合并了、这玩意会改变原有的接口
interface TSED {
    b:string;
}


let sa:TSED = {
    a:'', // 断言的时候TSED有的必须要有。 接口的合并
    b: '',
} as TSED

interface MSAY {
    [x:number]:any
}

let ak:MSAY = [1,2,3]


// [x:string]:any 支持 symbol
interface FQA {
    a:string;
}
let faqs:FQA['a'] = ''; // 原子取类型


interface SPACK {
    readonly name:string;
    // 用接口来形容类的时候void 表示不关心返回值。 这里是void 下面那个愿意返回什么就返回什么 如果这里是void 下面实例话后.不动 实现的时候如果返回了具体的类型会有.出来
    say():void
}

class ISPACK implements SPACK {
    readonly name: string = '';
    say(): string { // 接口定义了void 这里是可以有返回值的。
        return ''
    }
    
}


// 抽象类
// 抽象类可能存在抽象方法不能new它
abstract class abstractAK {
    say(){

    }
    abstract say2():void
}

class mQQQ extends abstractAK {
    say2(): void {
        throw new Error("Method not implemented.");
    }

}


class CGZ {

}

// 接口描述构造函数
interface GZ {
    // 描述构造函数
    new (name:string):CGZ // 描述构造函数最后返回一个 CGZ 的实例
}

function getGZ(c:CGZ){

}

// c:{new (name:string):CGZ}
// c: new (name:string)=>CGZ
// 这三种方式都可以
getGZ(new CGZ());


// 泛型
interface FX<T> {
    new (name:string):T
}


// 类型不确定，在执行的时候才能确定的时候用
// 泛型的推断
function createAy<T>(time:number, val:T):T[] {
    // T 就被推断为numer了
    // 但是这里的T不能用numer的方法内部不知道是个调用的时候才能知道是个啥
    let R:T[] = [];


    return R;
}
const RK = createAy(123, 1213); // 这里推断才知道T是个number // 调用的时候知道什么类型所以就知道返回的是个啥 RK: number[]；


function swap<T,K>(
    sa:[T,K]
):[K,T]{

    return [
        sa[1],
        sa[0],
    ]
}

const Mq = swap([1,'']); // 根据 [1, ''] 推断出 T K 类型 // 推断对号入座。
Mq[0].charAt // 调用玩了知道是个啥就知道应该有什么方法了。


interface OldShit{
    <T>(a:T):T[]; // T只对描述的函数有用。
}


// 表达式函数
interface bidaoshi {
    <T>(num:T):T
}

let fn:bidaoshi = <T>(s:T):T => {
    return s;
}

let qs = fn(1); // 调用的时候靠着推断知道的T是个number


// 泛型约束
function sumed<T extends string>(a:T,b:T):T{

    // a+b相加完了是个什么鬼
    // 如果T 是 string 就可以了最后在断言下，因为未知类型相加不知道是个什么东西，你要告诉它是什么
    // 你约束了T是个string所以可以相加但是加完了需要在重新断言下
    return (a+b) as T;
}
sumed('', '');


function getObjLen<T extends {length:number}>(obj:T):number{
    return obj.length;
}


function getVals<T extends object, K extends keyof T>(obj:T, key:K):T[K]{
    return obj[key]; 
}

let sqxed:keyof {a:1,b:''}; // 把所有的key 捣鼓出来搞成个联合关键词

let strings:keyof string; // string所有能点出来的都在这
let anys:keyof any; // number string symbol

getVals({a:1,b:2},'b');


// 类中的void 和函数中的void 不一样
// 接口中的void 实现的时候可以随便返回
// function mq():void{ return '' } 是不行的
interface MMPSQXIFC{
    say():void
}

class MMPSQXIFCCS implements MMPSQXIFC{
    say(): string { // 这里是行的
        return '';
    }

}


function MMPSQXIFCunc():void{
    return undefined; // 这样是可以的。
}


// never的使用场景

// 函数表达式不支持重载。

// this 放属性无法类型推断 所以用 composition.

// 从一个未知的对象把描述拿出来
let unkObj = {
    a:1,
    b:2,
    c:'',
    d: {
        a:1
    }
}

type getType = typeof unkObj; // 直接从实体类把描述能出来了 【类型反推】

let sqxgetType:getType = unkObj;

let sqxgetType2:typeof unkObj;

let sqxgetType3:Array<typeof unkObj>;


class Persions {

}


function getClasses(classed:typeof Persions) {

}

getClasses(Persions);


function getClasses2<T>(classed:new ()=>T) { // 注意这里不能typeof T  T是个类型不是值 typeof 后面必须是指

}

getClasses2(Persions);



// c:Persions 只能接受 new Persions()  c:new ()=>Persions 接受 Persions   Persions 和 typeof Persions 不是一个

// sqxgetType.d.a


class MapReduc<T> {
    add(val:T):T{
        return val
    }
}

let MapReducic = new MapReduc<number>();
let MapReducicss = MapReducic.add(1); // MapReducicss 在上面没有初始化泛型是无法推断的所以是unknow


// 类型保护
/**
 * 
 * @param val 
 * 
 * 1. typeof
 * 2. instanceof
 * 3. in 语法
 * 4. 可是比类型
 * 5. is语法
 * 6. null 保护
 * 
 */
function lxbh(val:string|number) {
    if(typeof val == 'string') {
        // 类型保护区块 typeof 关键字
        // string的区域
    } else {
        // number的区域
    }


}

// 可识别
interface ksb {
    kind:'a';
    eat():void
}
interface ksb2 {
    kind:'b';
    say():void
}

function getKSb(val:ksb|ksb2){
    if(val.kind == 'a') { // 每个类型有个公用的字段用来区分，用公用的字段来识别。
        val.eat();
    } else {
        val.say()
    }
}

function isString(val:unknown): val is string {
    return Object.prototype.toString.call(val) == '[object String]';
}
let jx = 'x'
if(isString(jx)) {
    // jx.charAt
}

function nullbaohu(n:number|null){
    if(n){
        // n.toFixed

        function iner(){ // 问题是不在一个作用域所以鸡鸡了
            // 需要再来一判读
            if(n) {
                // n.toFixed
            }
            // n?.toFixed 这里就猜测不出来了
        }
    }
}


// never的完整行校验
interface neverA {
    kind:'a';
    a():void
}
interface neverB {
    kind:'b';
    b():void
}
interface neverC {
    kind:'c';
    c():void
}


const assert = (obj:never) => {throw new Error('err')}

function rg(mq:neverA|neverB|neverC) {
    switch(mq.kind) {
        case 'a':mq.a();break;
        case 'b':mq.b();break;
        case 'c':mq.c();break;
        // 上面的|的所有情况都走了下面才能走不到否则报错 neverA|neverB|neverC 会一个个的进来看看会不会走到default走到了就错了。
        default:assert(mq); // 上面如果有漏掉的这里需要做兜底
    }
}


class neverCinc implements neverC{
    public kind:'c' = "c";
    c(): void {
        throw new Error("Method not implemented.");
    }

}
rg(new neverCinc())


// 交叉类型
interface JA {
    a:number;
    x:number; // 两个字短相同类型不一样相交有问题
    [x:string]:any
}
interface JB {
    b:number;
    x:string;// 两个字短相同类型不一样相交有问题
}
interface JC {
    b:number;
    [x:string]:any
}

// let JK: JA & JB = {
//     a:1,
//     b:2,
//     // x:'', 报错 既不能是number也不能是string 是个never不可用
// };

// 这个玩意扩展属性可以用扩展属性。 {} & {length:string}

// 兼容性接口 成员多的可以赋值给成员少的
function mixin<T extends object,K extends object>(obj:T, obj2:K):T & K{

    let tmp: T & K = {
        ...obj,
        ...obj2
    } as (T & K);

  
  
     
    return tmp;
}
let r = mixin({a:1}, {b:2}); // 注意相同字段不同类型never的情况




// 交叉类型的兼容性。
// 类型可以相互赋值
// let stred:string = {toString:()=>{}}  //这个不行因为string有很多方法 少的不能给多的
type MyStred = {toString():string};
let stred:MyStred = 'aaa' // 可以因为'aaa'是string有很多方法也包括了toString所以是行的。
// 对象多的可以赋值给少的只要满足少的基础描述就行了。


// 函数的兼容性 少的可以赋值给多的
// 参数 返回值 协变
let sum1 = (a:number, b:number):number => {
    return a+b;
}

let sum2x = (a:number):number => {
    return a;
}

// 针对形参来算的。

// sum1 = sum2x; 可以少的给多的

// sum2x = sum1 多的给少的是不行的

// 缩小范围是可以的。
function forEach<T>(arr:T[], cb:(item:T, idx:number, ay:T[])=>void){

}

forEach([1,2,3,4], (item, idx, ay) => {

})

// cb 少的给多的
forEach([1,2,3,4], (itemy) => {

})
// 多的给少的报错。
// forEach([1,2,3,4], (itemy,a,b,c) => {

// })

// 函数返回知道值的约束
// string | number = string 还是小的可以赋值给多的。
// 如果返回的是对象 多的可以赋值给少的。 参数和返回值如果是对象就按照对象的来 多的给少的。


// 针对参数的类型做兼容性。
// 逆变 和 协变
// 逆变 【子-》父亲】 协变【父->子】

class YY {
    name!:string;
}
class BB extends YY {
    age!:number;
}
class SS extends BB {
    sz!:string;
}

function getFn(cb:(b:BB)=>BB) {

}

// getFn(
//     (bb:BB) => { // 正常的要啥给啥
//         return bb;
//     }
// )

getFn(
    (bb:YY) => { // 正常的要啥给啥 参数儿子接受父亲的参数 父->子 协变
        return new SS; // 返回值多的给少的。 子->父 逆变 返回值只能是儿子或者孙子
    }
)


// getFn(
//     (bb:SS) => { // 正常的要啥给啥 参数儿子接受父亲的参数 父->子 协变
//         return new SS;
//     }
// ) // 这里把strict 改成false就可以传孙子这叫双向协变 或者 strictFunctionTypes

function getType(cb:(val:string|number)=>string) {

}

// getType((val:string)=>'')  这昂是不允许的 string|number 和 string 不存在父子关系。  string|number|boolean 是可以的


// 条件选择 ? ts的三元表达式
interface SY {
    a: string
}

interface SYB {
    b: string
}

interface Fly {
    c:string;
}

type MkS<T> = T extends SY ? SY : SYB;

type myMKS = MkS<SYB|SY>; // 返回的是 SY｜SYB 的联合类型。 SYB 走一次 SY 走一次 这个叫条件的分发 这里不能是 SYB & SY 它会把 SY & SYB 组成一个新的在走这个。 never extends xx true & 没有分发的功能

let imyMKS:myMKS = {
    b:'',
    a: '',
}


// TS 中内置的类型
// Exclude

type MyExclude<T,K> = T extends K ? never : T; // 如果返回never就不会出现在联合类型里面了

type ExcludeType = MyExclude<string|number|boolean, boolean>


// 多个属性抽离某几个 Extract
type MyExtract<T,K> = T extends K ? T : never;
type IMyExtract = MyExtract<string|number, string>


// 多个类型排除null类型NonNullable
type myNonNullable<T> = T extends null ? never : T; 



// infer 占位符
// 拿到函数的返回值类型 ReturnType
// infer 要配合 extends
function sayFn():string{
    return '';
}
type myReturnType<T extends (...args:any)=>any> = T extends ()=>infer R ? R : never

type fmyReturnType = myReturnType<()=>string>;

// 获取推断的参数
// type pms = Parameters
type myParameters<T extends (...args:any[]) => any> = T extends (...args:infer R) => any ? R : never // 放在哪就推断谁
type imyParameters = myParameters<(a:number,b:string)=>string>; // 返回的是个元祖
let ipcimyParameters:imyParameters = [1,''];

// 获取构造函数的参数ConstructorParameters
class ConstructorParameterss{
    constructor(a:number){

    }
}
type myConstructor = ConstructorParameters<typeof ConstructorParameterss>;

type MMConstructorParameters<T extends abstract new (...args:any[])=>any> = T extends abstract new (...args:infer R) => any ? R : never;

// 获取实例
type ised = InstanceType<typeof ConstructorParameterss>


// ts其他的内置类型
// 批量修改 Partail 全部变成可选的。
interface PLUP {
    name:string;
    age:number;
}

type MYPLUP<T> = {
    [P in keyof T]?: T[P] extends object ?  MYPLUP<T[P]> : T[P]// keyof T 得到接口的属性名字 P in 遍历 P每一项 T[P] 返回每一项对应的类型。 这个是单层可选没有递归
}
type IMYPLUP = MYPLUP<PLUP>;


// 可选的变成必填的 Required 如何把原来可选的变成非可选的 -? 表示去掉可选

// Readonly 全部加上readonly

// Pick 选出内容 在对象中选择属性 extract类型中选择类型
// 接口选择一个属性
type MyPick<T, K extends keyof T> = { // 联合类型不需要keyof了
    [P in K]: T[P]
}


// Omit 忽略属性 T & K 会出现never 解决这个事情
// 只修改接口中的某一个属性为可选的。 本意是 name 直接就消失了 pick留下这个消失
type MyOmit<T, K extends keyof T> = {
    // [P in keyof T]: P extends K ? never : T[P] // 这么玩属性无法消失

    [P in Exclude<keyof T, K>]: T[P]
}
type UseOmit = Omit<PLUP, 'name'> 


// 描述对象类型 Record 记录类型
// key:val 
let obj:Record<string, any> = {

}


// map方法


// K 代表 string number symbol
// V 值对应的类型
// 描述了函数的返回值是X cb 的返回值是X 回调函数都可以这么捣鼓。 X 靠着后面调用才知道的

function myMaps<K extends keyof any,V,X extends keyof any>(
    obj:Record<K, V>,
    cb:(
    val:V,
    k:K
    )=> X
):Record<K, X>{
    // X 是 cb的返回值
    let result= {} as Record<K, X>;

   for(let i in obj) {
    console.log(obj[i])
    let objdata = cb(obj[i], i);

    result[i] = objdata;
   }

    return result;
}

let rxmap = myMaps({
    name:'1',
    age:1
}, (item, key) => {
    return `$${item}`
})


/**
 * type Record<K extends keyof any, T> = {
    [P in K]: T;
};
key 只能是 string number symbol 的其中之一。
 
 */

// 装包
let zbobj = {
    name:'',
    age:''
}

// 变成
let zbobj2 = {
    name: {
        get(){

        },
        set(){

        }
    }
}


type ProxyIfyIFC<T extends object> = {
 // 把T进行一个包装
  [P in keyof T]: {
    get():T[P],
    set(val:any):void
  }
}

// 装包
function proxyify<T extends object>(data:T):ProxyIfyIFC<T>{
    let result = {} as ProxyIfyIFC<T>

    for(let i in data) {
        var val = data[i];
        result[i] = {
            get(key = ''){
                return val;
            },
            set(nval){
                val = nval;
            }
        }
    }

    return result;
}

let proxyData = proxyify(zbobj);
// proxyData.name.get()
// proxyData.name.get();


function unProxyify<T extends object>(obj:ProxyIfyIFC<T>):T {
    let result = {} as T;

    for(let i in obj) {
        result[i] = obj[i].get();
    }

    return result;
}

let data2 = unProxyify(proxyData);

// 演变的类型 -- Diff
// 获取2个类型的差集
interface ps01 {
    name:string;
    age:string;
    address:string;
}

interface ps02 {
    address:string;
}

// 如何求得 name age
type PSWD<T extends object, K extends object> = {
    // keyof T
    // keyof T
    // keyof K
    // keyof K
    // 排除
    [P in Exclude<keyof T, keyof K>]: T[P]

    // 遍历
}

type MYPSWD = PSWD<ps01, ps02>

// 交集
type PSWD2<T extends object, K extends object> = {
    // keyof T
    // keyof T
    // keyof K
    // keyof K
    // 排除
    [P in Extract<keyof T, keyof K>]: T[P]

    // 遍历
}
type MYPSWD2 = PSWD2<ps01, ps02>


// 两个对象合并出现 never问题 T & K
interface TNVA {
    name:string
}
interface TNVA2 {
    name:number;
}

type TVD = TNVA & TNVA2 
// let tvd:TVD = {
//     // name name就是never并不知道到底是什么
// }

// 两个对象合并以后者为主
type computed<T>  ={
    [P in keyof T]:T[P]
}
type Merge<T extends object,K extends object> = computed<PSWD<T, K> & PSWD<K, T> & PSWD2<K,T>> // 展开了看着更清晰 类型展开方便提示


type Merge2<T extends object,K extends object> = Omit<T, keyof K> & K


// 命名空间
// 解决命名冲突，会有调用过长的问题。
namespace Home {
    // 没有对外暴露需要加上export
    export class Dog {
        
    }
}

namespace Home {
    class Dog {
        
    }
}

// Home.Dog

// 内部模块用命名空间来声明 解决同一个文件下的命名

// 命名空间重名会发生命名空间合并。合并后重名的会报错。 前提是要都导出如果一个导出另一个不导出不报错。 只对对外暴露的看是否重复

// 命名空间的实现
var Homes = {};
(function(Home:any){
    Home.Dog = '1' // 直接往对象鼓捣
})(Homes)


// module
module Home { // 这样也是可以的但是最后还是希望写成namespace 写声明文件的时候只能用module
    class Dog {
        
    }
}


// 命名空间是可以嵌套的
namespace ns {
    export namespace ns2 {
        export class Cat{}
    }
}
// ns.ns2.Cat

// namespace 和 module 是内部  import export 是外部的

// 外部模块
// ESModule commomjs  amd 
// esmodule 和 commonjs 是否可以混用可以但是需要特殊的写法。


// 这种语法默认是不兼容的。
// module.exports = {a:1} a.js
// import xx from 'a'; // 引用的时候有问题。

// export default 'xx'
// const s = require('xx') s.default

// ts config esmoduleInterop 默认会帮着处理兼容性

// rollup 默认只支持es的语法 默认支持tree shaking 
// import a from "./index.js"; node 的语法导出的这里不认识
// let rpced = require("./index.js");  commonjs 的require是没有语法提示的

// ts 为了支持 commomjs 搞了个一个新语法export = 

// import reeeeee = require("./ma"); // 这个需要改tsconfig 的module 为commonjs 这个rollup就挂了
// import * as cmd from "./ma";
// const cmd = require("ma"); 
// console.log(cmd); // cmd就是个any

// import xeeee = require("./ma"); 配套 导出 export = 配套 tsconfig module为commomjs

// 除非引入的模块不是ts鼓捣的用export = 引入用 import x = require("xx")

// es6模块和commomjs的模块不兼容。
// 别人用commomjs写的你用es6引入就会有这个问题所以就出现了 export = 和 import x = require() 这个套路。 倒入 用 import x = 或者 import x from 都行 别忘了tsconfig module commomjs
// 注意这个玩意和rollup冲突


// 怎么做声明文件 
// 引入三方库后需要引入对应@types/xxx
// 比如 import jquery from "jquery"; 就报错了。 tsconfig.json moduleResolution node 按照node 的解析方式去找声明文件。 @types/jquery 别人写好的声明文件。
// 或者自己声明一个 declare module 'jquery' 去 node_modules/@types 下面找。
// export = jquery 因为是个node的模块所以用export = 去导出，/// <reference path='xxx.d.ts' /> 引入声明文件 <reference types="sizzle" /> 直接引入声明模块 引入声明文件的依赖

// declare 是全局的声明

declare let ak34:string;

declare interface MapReduces {
    name:string;
}

let mprdc:MapReduces = {
    name: '',
}

import jQuery from "jquery";

// 在用的时候没有声明就考虑要自己来一个了声明了



// 声明资源类的
// 通过import 进来的都是module declare中的内容不需要导出默认就导出了

import aked1122 from "./a.png";
// aked1122.say();
// aked1122.a

// 如果不存在的模块ts无法识别就declare module一下让他识别

// declare 名字冲突了怎么处理怎么合并
// 1.接口重名默认执行合并。
// 2.命名空间也能合并。
// 3.函数和命名空间能合并。

// 创建types/index.d.ts 的命名空间。
// 1. moduleResolution 2. baseUrl  3. paths: {"*": ["types/*"]} //制定声明文件来什么地方找。
// 逻辑是先按照 moduleResolution node 的方式找，找不到在用paths找。 注意xx.d.ts xx要和引入到包的那个名字相同。 比如jquery.d.ts
// 这样不需要 declare module "jquery" 直接 在 jquery.d.ts 里面写声明 declare function $():void  export default $就行了



// unknow 是 any 的安全类型。
let uwk:any = 1;
uwk.say()
let uwk2:unknown = 1;
// uwk2.cc() unknow 不能随便调用方法。
// 在函数中如果不希望调用这个值的属性全部用unknow


// 原来没有的你去扩展会有问题
// 用接口扩展原来的方法接口可以和接口合并
// String这玩意默认就是个接口 在原来接口上在扩展就行了接口和接口的合并
String.prototype.double = function() {

}

// 合并发生在全局下所以 在 d.ts才能合并全局暴露全局合并。



// 注意一个ts文件有了import 或者 export 都叫模块 不对外了

// 如果非要在这个文件下鼓秋可以用declare global

declare global { // 在模块内部直接公开到全局上 在全局上扩展 
    interface String {
        mq():void
    }
}

String.prototype.mq = function() {

}

// UMD库的d.ts声明文件注意事项
/**
 * 格式要求
UMD库的d.ts声明文件需要满足以下格式，在VS code中和编译后才不会报错。
declare namespace XXX{ }; export as namespace XXX; export=XXX;
引入js库方式：
import XXX=require("./xxx.js)；
 */